use std::{collections::HashSet, path::PathBuf};

use serde::{Deserialize, Serialize};
use tokio::fs;

use crate::{
    app::{FileProcessResult, make_file_metadata_path_name},
    error::FileStoreError,
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PendingDownloadRecord {
    pub file_id: u64,
    pub original_file_name: String,
    pub download_path: PathBuf,

    // store chunk_id
    pub downloaded_chunks: HashSet<usize>,
}

impl PendingDownloadRecord {
    pub fn new(file_id: u64, original_file_name: String, download_path: PathBuf) -> Self {
        Self {
            file_id,
            original_file_name,
            download_path,
            downloaded_chunks: HashSet::new(),
        }
    }

    pub fn key(&self) -> Vec<u8> {
        self.file_id.to_be_bytes().to_vec()
    }

    pub async fn save_metadata(&self, metadata: &FileProcessResult) -> Result<(), FileStoreError> {
        fs::create_dir_all(&self.download_path).await?;
        let result_file = std::fs::File::create(make_file_metadata_path_name(&self.download_path))
            .map_err(FileStoreError::IOError)?;
        serde_cbor::to_writer(result_file, &metadata).map_err(FileStoreError::CborError)
    }

    pub fn load_metadata(&self) -> Result<FileProcessResult, FileStoreError> {
        let result_file = std::fs::File::open(make_file_metadata_path_name(&self.download_path))
            .map_err(FileStoreError::IOError)?;
        serde_cbor::from_reader(result_file).map_err(FileStoreError::CborError)
    }
}

impl TryInto<Vec<u8>> for PendingDownloadRecord {
    type Error = serde_cbor::Error;

    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
        serde_cbor::to_vec(&self)
    }
}

impl TryFrom<Vec<u8>> for PendingDownloadRecord {
    type Error = serde_cbor::Error;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        serde_cbor::from_slice(&value)
    }
}
